home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1995-06-20 | 30.3 KB | 1,011 lines | [ TEXT/MWPS]
{$N-} { Server Controller code } { © Copyright 1991-1995 Jim Luther, All rights reserved. } { Modification History: } { 28 Dec 91 JML V1.0d1 First working version with System 7 File Sharing } { 30 Dec 91 JML V1.0d1 Added check for Gestalt and ServerDispatch traps to } { InitializeApp and added kExitNoServerDispatch error string } { resource. } { 30 Dec 91 JML V1.0d1 Added gServerKind and gASFileServerFSSpec globals and } { GetServerKind and FindASFileServerApp functions. } { 30 Dec 91 JML V1.0d1 Changed RemoteSCPBRec structure (added scSetup field) } { 30 Dec 91 JML V1.0d1 Hacked up PPCProcessReads to use SCSleepServer and SCWakeServer } { when the server is AppleShare 3.0. } { 31 Dec 91 JML V1.0d1 PPCProcessReads now launches the AppleShare File Server } { application if the SCStartServer command is sent. } { 31 Dec 91 JML V1.0d2 Works with AppleShare 3.0. } { 2 Jan 92 JML V1.0d2 Got my creator assignments, so I added a BNDL, ICN#, etc and } { changed a few things to use the new creator types and the } { version resources. } { 10 Mar 93 JML V1.0 Ahhh, just release the dang thing. It works fine. } { 21 May 95 JML V1.1b1 Make it compile with Metrowerks Pascal CW6 and } { Universal Interfaces (what a hassle!). } { 21 May 95 JML V1.1b1 Change kMaxSleep from MaxLongInt to 600 to work around } { old WakeUpProcess bug. } { 22 May 95 JML V1.1b1 Set gPPCWriteInProgress to false everytime in WriteCompProc to } { fix hang condition. } {$IFC UNDEFINED THINK_Pascal} {$ELSEC} {$I-} {$ENDC} PROGRAM ServerControl; USES AppleTalk, Processes, PPCToolBox, EPPC, Notification, AppleEvents, Script, Finder, Folders, Traps, {$IFC UNDEFINED THINK_Pascal} ToolUtils, GestaltEqu, Resources, SegLoad, {$ENDC} ServerControlIntf; CONST kPortName = 'Server Controller'; { should use a resource string } kControllerCreator = 'ASsc'; { used for our PPC port's portCreator and nbpType } kRemoteCreator = 'ASrc'; { used for remote PPC port's portCreator } kMaxSleep = 600; { sleep for around 10 seconds when not connected } kConnectedSleep = 60; { how often to poll server when connected } { PPCReject rejectInfo codes } kRemoteIsNotOwner = 1; kRemoteAppUnknown = -1; kExitErrorStrings = 500; kNumExitErrors = 12; { IMPORTANT -- Must match number of exit errors } { ExitToShell errors } kExitNoSystem7 = 1; kExitNoAppleEvts = 2; kExitAEHandlerNotInstalled = 3; kExitNoOwnerName = 4; kExitNoPPC = 5; kExitPPCInitFailed = 6; kExitAppleTalkDisabled = 7; kExitProgramLinkingDisabled = 8; kExitPPCOpenFailed = 9; kExitPPCInformFailed = 10; kExitNoServerDispatch = 11; kExitCantFindASFSApp = 12; kCantIncreaseStack = 13; {ServerControl constants} kSIMaxLogins = 200; kVerNumType = $01008000; { NumVersion.version used for the remote and our } { PPC ports' portType. In this case version 1.0.0 final. } TYPE RemoteSCPBRec = RECORD scPB: SCParamBlockRec; scMessageOrName: Str255; scDiscArray: ARRAY[1..kSIMaxLogins] OF LongInt; scSetup: SetupInfoRec; END; PPCIOBuffer = RemoteSCPBRec; VAR gQuit: Boolean; gSleep: LongInt; gTicks: LongInt; gServerType: Integer; { 0 = System 7 File Sharing; 1 = AppleShare 3.0 } gASFileServerFSSpec: FSSpec; { FSSpec to the AppleShare File Server app if } { gServerType = AppleShare 3.0 } gOwnerName: Str255; gOurPSN: ProcessSerialNumber; gPPCPortOpen: Boolean; { TRUE when the PPC port has been opened } gPPCPortRefNum: PPCPortRefNum; { The PPC port reference number } gPPCSessRefNum: PPCSessRefNum; { The PPC session reference number } gPPCGeneralRec: PPCParamBlockRec; gPPCSessPortName: PPCPortRec; gPPCSessLocationName: LocationNameRec; gPPCSessUserName: Str32; gPPCReadBuffer: PPCIOBuffer; gPPCDataRead: Boolean; gRemoteSCpb: RemoteSCPBRec; gPollSCpb: SCParamBlockRec; gPPCWriteRec: PPCParamBlockRec; { used for PPCWrite calls } gPPCWriteInProgress: Boolean; gNotificationMgrPresent: Boolean; gNMRec: NMRec; gNMStrs: ARRAY[1..kNumExitErrors] OF Str255; { needed forward declarations } PROCEDURE ReadCompProc (pb: PPCParamBlockPtr); FORWARD; FUNCTION StartPPCInform (pb: PPCParamBlockPtr): OSErr; FORWARD; {$S Main} PROCEDURE NotifyResponseProc (nmReqPtr: NMRecPtr); VAR oldA5: LongInt; err: OSErr; BEGIN oldA5 := SetA5(nmReqPtr^.nmRefCon); gQuit := TRUE; err := WakeUpProcess(gOurPSN); err := NMRemove(nmReqPtr); oldA5 := SetA5(oldA5); END; {$S Main} PROCEDURE NotifyAndExit (errCode: Integer); BEGIN WITH gNMRec DO BEGIN qType := ORD(nmType); nmMark := 0; nmIcon := NIL; nmSound := Handle(-1); nmStr := @gNMStrs[errCode]; nmResp := @NotifyResponseProc; nmRefCon := SetCurrentA5; END; IF (NMInstall(@gNMRec) <> noErr) THEN BEGIN gSleep := 10; gQuit := TRUE; END; END; {-------------------------------------------------------------------------------} {| IMPORTANT NOTE } {| From here down to the next comment that looks like this (i.e. starts with } {| an IMPORTANT NOTE title) all the routines defined are completion routines } {| the main trick is that they almost always call some other PPC Toolbox call } {| or somehow tell the main application that it's time to process some data } {-------------------------------------------------------------------------------} {------------------------------------------------------------------------------} {$S Main} PROCEDURE EndCompProc (pb: PPCParamBlockPtr); { This procedure gets called when the asynchronous PPCEnd call completes. } VAR err: OSErr; { used to catch the PPC function results. } BEGIN IF gPPCPortOpen THEN BEGIN gSleep := kMaxSleep; { go back into sleepy mode while no session is active } { use the parameter block to start a new session } err := StartPPCInform(pb); IF err <> noErr THEN BEGIN { Should signal the application that "something really bad" has} { happened and enqueue the parameter block so it doesn't} { get lost.} END; END; { ELSE port isn't open; things are shutting down so do nothing } END; {------------------------------------------------------------------------------} {$S Main} PROCEDURE WriteCompProc (pb: PPCParamBlockPtr); { This procedure gets called when the asynchronous PPCWrite call completes.} { If no errors are detected, then it puts the parameter block in the } { gWpbQueue where the PollTheServer procedure will find it next time it } { needs to send data to the remote. } { If an error is detected, then PPCEnd is called asynchronously to close} { the session. } VAR err: OSErr; { used to catch the PPC function results. } BEGIN gPPCWriteInProgress := FALSE; IF PPCWritePBPtr(pb)^.ioResult <> noErr THEN BEGIN { if we get an error, then we call PPCEnd to close up cleanly} PPCEndPBPtr(pb)^.ioCompletion := @EndCompProc; err := PPCEndAsync(PPCEndPBPtr(pb)); END; END; {------------------------------------------------------------------------------} {$S Main} PROCEDURE ReadCompProc (pb: PPCParamBlockPtr); { This procedure gets called when the asynchronous PPCRead call completes.} { If no errors are detected, then it puts the parameter block in the } { gRpbQueue where the PPCProcessReads procedure will find it and process } { the data read. PPCProcessReads will make another PPCRead call. } { If an error is detected, then PPCEnd is called asynchronously to close} { the session. } VAR err: OSErr; { used to catch the PPC function results. } BEGIN IF PPCReadPBPtr(pb)^.ioResult = noErr THEN BEGIN gPPCDataRead := TRUE; err := WakeUpProcess(gOurPSN); END ELSE BEGIN { if we get an error, then we call PPCEnd to close up cleanly} PPCEndPBPtr(pb)^.ioCompletion := @EndCompProc; err := PPCEndAsync(PPCEndPBPtr(pb)); END; END; {------------------------------------------------------------------------------} {$S Main} PROCEDURE RejectCompProc (pb: PPCParamBlockPtr); { This procedure gets called when the asynchronous PPCReject call completes. } VAR err: OSErr; { used to catch the PPC function results. } BEGIN IF gPPCPortOpen THEN { if port isn't open, then things are shutting down so do nothing } BEGIN { use the parameter block to start a new session } err := StartPPCInform(pb); IF err <> noErr THEN BEGIN { Should signal the application that "something really bad" } { has happened and enqueue the parameter block so it } { doesn't get lost.} END; END; END; {------------------------------------------------------------------------------} {$S Main} PROCEDURE AcceptCompProc (pb: PPCParamBlockPtr); { This procedure gets called when the asynchronous PPCAccept call completes.} { If no errors are detected, then it makes an asynchronous PPCRead call.} { If an error is detected, then it makes an asynchronous PPCEnd call to close} { the session. } VAR err: OSErr; { used to catch the PPC function results. } BEGIN IF PPCAcceptPBPtr(pb)^.ioResult = noErr THEN BEGIN gPPCSessRefNum := PPCAcceptPBPtr(pb)^.sessRefNum; gSleep := kConnectedSleep; { start the first PPCRead } WITH PPCReadPBPtr(pb)^ DO BEGIN ioCompletion := @ReadCompProc; { We're reusing the same parameter block, so the sessRefNum} { is already filled in for us. } bufferLength := sizeof(PPCIOBuffer); bufferPtr := @gPPCReadBuffer; END; gPPCDataRead := FALSE; err := PPCReadAsync(PPCReadPBPtr(pb)); { asynchronously } err := WakeUpProcess(gOurPSN); END ELSE BEGIN { if we get an error, then we call PPCEnd to close up cleanly} PPCEndPBPtr(pb)^.ioCompletion := @EndCompProc; err := PPCEndAsync(PPCEndPBPtr(pb)); END; END; {------------------------------------------------------------------------------} {$S Main} PROCEDURE InformCompProc (pb: PPCParamBlockPtr); { This procedure gets called when the asynchronous PPCInform call completes.} { If no errors are detected, then it decides if the session request} { should be accepted of rejected. If the session is accepted, then it makes an} { asynchronous PPCAccept call. If the session is rejected, then it makes an} { asynchronous PPCReject call (and passes PPCReject the rejectInfo). If an} { error is detected, then it makes an asynchronous PPCEnd call to close} { the session. } VAR err: OSErr; { used to catch the PPC function results. } BEGIN IF PPCInformPBPtr(pb)^.ioResult = noErr THEN BEGIN { Make sure the owner is connecting with my remote application } IF (EqualString(gPPCSessUserName, gOwnerName, FALSE, TRUE) OR (PPCInformPBPtr(pb)^.requestType = CHAR(ppcLocalOrigin))) AND (PPCInformPBPtr(pb)^.portName^.portKindSelector = ppcByCreatorAndType) AND (PPCInformPBPtr(pb)^.portName^.portCreator = kRemoteCreator) AND (LongInt(PPCInformPBPtr(pb)^.portName^.portType) = kVerNumType) THEN BEGIN PPCAcceptPBPtr(pb)^.ioCompletion := @AcceptCompProc; { We're reusing the same parameter block, so the} { sessRefNum is already filled in for us. } err := PPCAcceptAsync(PPCAcceptPBPtr(pb)); { asynchronous } END ELSE BEGIN WITH PPCRejectPBPtr(pb)^ DO BEGIN ioCompletion := @RejectCompProc; { We're reusing the same parameter block, so the} { sessRefNum is already filled in for us. } { Set the rejectInfo field } IF gPPCSessUserName <> gOwnerName THEN rejectInfo := kRemoteIsNotOwner ELSE rejectInfo := kRemoteAppUnknown; END; err := PPCRejectAsync(PPCRejectPBPtr(pb)); { asynchronous } END; END ELSE BEGIN { if we get an error, then we call PPCEnd to close up cleanly} PPCEndPBPtr(pb)^.ioCompletion := @EndCompProc; err := PPCEndAsync(PPCEndPBPtr(pb)); END; END; {------------------------------------------------------------------------------} {| IMPORTANT NOTE:} {| All the procedures above here are executed at interrupt time. Each} {| one of them is used as a completion routine.} {-------------------------------------------------------------------------------} {$S Main} FUNCTION StartPPCInform (pb: PPCParamBlockPtr): OSErr; { Initializes the session record's fields, and calls PPCInform to receive} { another PPC session request.} { Any errors detected are passed back to the caller. } VAR err: OSErr; BEGIN { set up the parameter block for a PPCInform call } WITH PPCInformPBPtr(pb)^ DO BEGIN ioCompletion := @InformCompProc; portRefNum := gPPCPortRefNum; autoAccept := FALSE; { we will approve sessions before accepting them } { The portName, locationName, and userName records } { are filled in by PPCInform. They tell you the } { port name, location name, and user name of the } { destination port that is attempting to start a } { session with us. } portName := @gPPCSessPortName; locationName := @gPPCSessLocationName; userName := @gPPCSessUserName; END; { execute PPCInform asynchronously and return any errors to caller } StartPPCInform := PPCInformAsync(PPCInformPBPtr(pb)); END; {$S Main} FUNCTION OpenPPCPort: OSErr; { OpenPPCPort opens a PPC port for use by the server sessions.} { It initializes the port name and location name records.} { Then, it calls PPCOpen synchronously to open the port. If the port was} { sucessfully opened, the gPPCPortOpen is set TRUE, and gPPCPortRefNum is set to} { the port reference number returned by PPCOpen.} { Any errors detected are passed back to PPCStartUp to be returned to} { the application. } VAR thePortRec: PPCPortRec; { the port name of the port to be opened. } theLocationName: LocationNameRec; { location name of the port to be opened. } theOpenPBRec: PPCOpenPBRec; { used by the PPCOpen call. } err: OSErr; { used to keep track of errors within the function. } BEGIN { initialize the port name record } WITH thePortRec DO BEGIN nameScript := GetScriptManagerVariable(smSysScript); { use Script Manager call to get System Script } name := kPortName; { This is the name that will show up in the } { "Programs" list that the Browser puts up.} { It should be a resource string instead of } { hard coded (as done here).} portKindSelector := ppcByCreatorAndType; { port kind by creator/type } portCreator := kControllerCreator; portType := OSType(kVerNumType); END; { initialize the location name record } WITH theLocationName DO BEGIN locationKindSelector := ppcNBPTypeLocation; nbpType := kControllerCreator; END; { Now, set up Open parameter block record } WITH theOpenPBRec DO BEGIN ioCompletion := NIL; { no completion Proc needed (synchronous) } serviceType := CHAR(ppcServiceRealTime); { 7.0 only supports this type of service } resFlag := 0; { must be zero } portName := @thePortRec; { pointer to port record} locationName := @theLocationName; { pointer to location name record } networkVisible := TRUE; { Yes, let other PPC users see us! } END; { execute PPCOpen synchronously and return any errors to caller } err := PPCOpen(@theOpenPBRec, FALSE); IF err = noErr THEN BEGIN gPPCPortOpen := TRUE; { set the global port open flag } gPPCPortRefNum := theOpenPBRec.portRefNum; { set the global port reference number } END; OpenPPCPort := err; END; {$S Main} PROCEDURE PPCShutDown; { PPCShutDown first closes the PPC port that was opened by PPCStartUp.} { Closing the port will automatically kill all sessions that use that port.} { After closing the port, PPCShutDown disposes of all session records. } VAR theClosePBRec: PPCClosePBRec; err: OSErr; BEGIN { Close the port. This will cause all PPC calls associated with this port } { to complete. } IF gPPCPortOpen THEN { close the port } BEGIN gPPCPortOpen := FALSE; { tell completion routines we're shutting down } { so they won't try to restart a session } theClosePBRec.ioCompletion := NIL; theClosePBRec.portRefNum := gPPCPortRefNum; err := PPCClose(@theClosePBRec, FALSE); END; END; {$S Initialize} FUNCTION InitPPCStuff: Boolean; VAR PPCAttributes: LongInt; {Storage for the response from Gestalt} err: OSErr; {Temporary variable to catch errors} BEGIN InitPPCStuff := FALSE; IF Gestalt(gestaltPPCToolboxAttr, PPCAttributes) <> noErr THEN BEGIN NotifyAndExit(kExitNoPPC); { ••• Bail out now ••• } Exit(InitPPCStuff); END; { ELSE PPC Toolbox is present } { Does PPC need initialization? } IF BAND(PPCAttributes, gestaltPPCSupportsRealTime) = 0 THEN BEGIN { PPC Toolbox needs initialization } { initialize the PPC Toolbox and set function result } IF PPCInit = noErr THEN { get the post-init attributes for the PPC Toolbox } err := Gestalt(gestaltPPCToolboxAttr, PPCAttributes) ELSE { PPC can't be inited } BEGIN NotifyAndExit(kExitPPCInitFailed); { ••• Bail out now ••• } Exit(InitPPCStuff); END; END; { Make sure ports can be opened to the outside world } IF BAND(PPCAttributes, gestaltPPCSupportsOutGoing) = 0 THEN { It's likely that AppleTalk is disabled, so you } { may want to tell the user to activate AppleTalk } { from the Chooser. } BEGIN NotifyAndExit(kExitAppleTalkDisabled); { ••• Bail out now ••• } Exit(InitPPCStuff); END; { Make sure ports can be opened with location names that the } { outside world can see } IF BAND(PPCAttributes, gestaltPPCSupportsIncoming) = 0 THEN { It's likely that Program Linking is disabled, so you } { may want to tell the user to start Program Linking } { from the Sharing Setup control panel. } BEGIN NotifyAndExit(kExitProgramLinkingDisabled); { ••• Bail out now ••• } Exit(InitPPCStuff); END; IF OpenPPCPort <> noErr THEN { couldn't open a PPC port } BEGIN NotifyAndExit(kExitPPCOpenFailed); { ••• Bail out now ••• } Exit(InitPPCStuff); END; IF StartPPCInform(@gPPCGeneralRec) <> noErr THEN BEGIN PPCShutDown; { close the port } NotifyAndExit(kExitPPCInformFailed); { ••• Bail out now ••• } Exit(InitPPCStuff); END; InitPPCStuff := TRUE; END; {$S Main} { This is the standard Open Application event. } FUNCTION AEOpenHandler (messagein: AppleEvent; reply: AppleEvent; refIn: LongInt): OSErr; BEGIN { we of course don't do anything here } AEOpenHandler := noErr; END; {$S Main} FUNCTION AEOpenDocHandler (messagein: AppleEvent; reply: AppleEvent; refIn: LongInt): OSErr; BEGIN { we of course don't do anything here } AEOpenDocHandler := errAEEventNotHandled; { we have no docs, so no odoc events should come to us } END; {$S Main} FUNCTION AEPrintHandler (messagein: AppleEvent; reply: AppleEvent; refIn: LongInt): OSErr; BEGIN { we of course don't do anything here } AEPrintHandler := errAEEventNotHandled; { we have no docs, so no pdoc events should come to us } END; {$S Main} { Standard Quit event handler, to handle a Quit event from the Finder, for example. } { ••••• DO NOT CALL EXITTOSHELL HERE ••••• or you will never have a happy life. } FUNCTION AEQuitHandler (messagein: AppleEvent; reply: AppleEvent; refIn: LongInt): OSErr; BEGIN { prepQuit sets the Stop flag for us. It does _NOT_ quit, you } { should NEVER quit from an AppleEvent handler. Calling } { ExitToShell here would blow things up } gQuit := TRUE; AEQuitHandler := noErr; END; {$S Main} { I'm not doing any error handling here because there's } { not a lot I can do; just pass the errors back. } PROCEDURE DoHighLevel (AERecord: EventRecord); VAR err: OSErr; BEGIN err := AEProcessAppleEvent(AERecord); END; {$S Main} PROCEDURE PPCProcessReads; {••••• My initial design was flawed so I'm gonna have to hack this up to get it } {••••• to work with AS 3.0 and System 7. 1.0 d3 will fix it. } VAR pbPtr: PPCParamBlockPtr; err: OSErr; launchPB: LaunchParamBlockRec; BEGIN IF gPPCDataRead THEN { process the data read } BEGIN { Make a server control call based on the data just read } { move the parameter block data into the local SCParamBlockRec } BlockMove(gPPCGeneralRec.readParam.bufferPtr, @gRemoteSCpb, sizeof(RemoteSCPBRec)); { set up any parameter block pointers and call ServerDispatch } CASE gRemoteSCpb.scPB.disconnectPB.scCode OF SCStartServer: BEGIN CASE gServerType OF 0: { System 7 File Sharing } err := SyncServerDispatch(@gRemoteSCpb.scPB); 1: BEGIN { This call won't happen unless the AppleShare File } { Server application isn't running, so we need to } { launch it. } WITH launchPB DO BEGIN launchBlockID := extendedBlock; launchEPBLength := extendedBlockLen; launchFileFlags := 0; launchControlFlags := launchContinue + launchNoFileFlags; launchAppSpec := @gASFileServerFSSpec; launchAppParameters := NIL; END; err := LaunchApplication(@launchPB); END; END; END; SCShutDown: BEGIN gRemoteSCpb.scPB.disconnectPB.scMessagePtr := @gRemoteSCpb.scMessageOrName; {••••• HACK ALERT!!! Will delete this IF statement at 1.0d3 } IF gServerType = 1 THEN gRemoteSCpb.scPB.disconnectPB.scCode := SCSleepServer; err := SyncServerDispatch(@gRemoteSCpb.scPB); END; SCCancelShutDown: BEGIN err := SyncServerDispatch(@gRemoteSCpb.scPB); END; SCWakeServer: BEGIN err := SyncServerDispatch(@gRemoteSCpb.scPB); END; END; { OK, the call was made, so fill out the readParam parameter block } { and call PPCRead } pbPtr := @gPPCGeneralRec; WITH PPCReadPBPtr(pbPtr)^ DO BEGIN ioCompletion := @ReadCompProc; bufferLength := sizeof(PPCIOBuffer); { full buffer size again } { We're reusing the same parameter block, so the sessRefNum } { and bufferPtr fields are already filled in for us. } bufferPtr := @gPPCReadBuffer; END; gPPCDataRead := FALSE; err := PPCReadAsync(PPCReadPBPtr(pbPtr)); { asynchronously } END; END; {$S Main} PROCEDURE PollTheServer; VAR err: OSErr; BEGIN IF NOT gPPCWriteInProgress THEN BEGIN gPollSCpb.pollServerPB.scCode := SCPollServer; IF SyncServerDispatch(@gPollSCpb) = noErr THEN BEGIN {now, send the gPollSCpb to the remote} WITH gPPCWriteRec.writeParam DO BEGIN ioCompletion := @WriteCompProc; sessRefNum := gPPCSessRefNum; bufferLength := sizeof(SCParamBlockRec); bufferPtr := @gPollSCpb; more := FALSE; { I'm not using userData, blockCreator, or blockType } END; gPPCWriteInProgress := TRUE; err := PPCWriteAsync(PPCWritePBPtr(@gPPCWriteRec)); END; END; END; {$S Main} PROCEDURE doNullEvt; VAR ticks: LongInt; BEGIN ticks := TickCount; IF ticks - gTicks >= gSleep THEN BEGIN IF gPPCSessRefNum <> 0 THEN BEGIN PPCProcessReads; PollTheServer; END; gTicks := ticks; END; END; {$S Main} PROCEDURE DoEventLoop; VAR evtRecord: EventRecord; bob: Boolean; BEGIN REPEAT bob := WaitNextEvent(highLevelEventMask, evtRecord, gSleep, NIL); CASE evtRecord.what OF nullEvent: doNullEvt; kHighLevelEvent: DoHighLevel(evtRecord); END; { CASE evtRecord.what } UNTIL gQuit = TRUE; END; { DoEventLoop } {$S Initialize} FUNCTION InitAEStuff: Boolean; VAR err: OSErr; BEGIN { The following series of calls install our AppleEvent Handlers. } { These handlers are added to the application event handler list } { that the AppleEvent manager maintains. So, whenever an } { AppleEvent happens and we call AEProcessEvent, the AppleEvent } { manager will check our list of handlers and dispatch to the } { the correct handler if there is one. } err := AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, @AEOpenHandler, 0, false); IF err = noErr THEN err := AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, @AEOpenDocHandler, 0, false); IF err = noErr THEN err := AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, @AEQuitHandler, 0, false); IF err = noErr THEN err := AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, @AEPrintHandler, 0, false); IF err <> noErr THEN NotifyAndExit(kExitAEHandlerNotInstalled); { ••• Bail out now ••• } InitAEStuff := (err = noErr); END; {$S Initialize} FUNCTION NumToolboxTraps: Integer; BEGIN IF NGetTrapAddress(_InitGraf, ToolTrap) = NGetTrapAddress($AA6E, ToolTrap) THEN NumToolboxTraps := $200 ELSE NumToolboxTraps := $400; END; {$S Initialize} FUNCTION GetTrapType (theTrap: Integer): TrapType; CONST TrapMask = $0800; BEGIN IF BAND(theTrap, TrapMask) > 0 THEN GetTrapType := ToolTrap ELSE GetTrapType := OSTrap; END; {$S Initialize} FUNCTION TrapAvailable (theTrap: Integer): Boolean; VAR tType: TrapType; BEGIN tType := GetTrapType(theTrap); IF tType = ToolTrap THEN BEGIN theTrap := BAND(theTrap, $07FF); IF theTrap >= NumToolboxTraps THEN theTrap := _Unimplemented; END; TrapAvailable := NGetTrapAddress(theTrap, tType) <> NGetTrapAddress(_Unimplemented, ToolTrap) END; {$S Initialize} FUNCTION FindASFileServerApp (VAR theFSSpec: FSSpec): OSErr; VAR err: OSErr; foundVRefNum: Integer; foundDirID: LongInt; pb: HParamBlockRec; spec1: CInfoPBRec; spec2: CInfoPBRec; BEGIN { get the vRefNum of the boot disk } err := FindFolder(kOnSystemDisk, kSystemFolderType, FALSE, foundVRefNum, foundDirID); { look for the AppleShare File Server application } WITH pb DO BEGIN ioCompletion := NIL; ioNamePtr := NIL; ioVRefNum := foundVRefNum; ioMatchPtr := FSSpecArrayPtr(@theFSSpec); ioReqMatchCount := 1; ioSearchBits := fsSBFlFndrInfo; { look only at ioFlFndrInfo} ioSearchInfo1 := @spec1; ioSearchInfo2 := @spec2; ioSearchTime := 0; ioCatPosition.initialize := 0; ioOptBuffer := NIL; ioOptBufSize := 0; END; WITH spec1.ioFlFndrInfo DO BEGIN fdType := 'APPL'; {••••• should be constant } fdCreator := 'hgfd'; {••••• should be constant } fdFlags := 0; fdLocation.v := 0; fdLocation.h := 0; fdFldr := 0; END; WITH spec2.ioFlFndrInfo DO BEGIN fdType := OSType(-1); fdCreator := OSType(-1); fdFlags := 0; fdLocation.v := 0; fdLocation.h := 0; fdFldr := 0; END; err := PBCatSearchSync(@pb); IF ((err = noErr) OR (err = eofErr)) AND (pb.ioActMatchCount > 0) THEN FindASFileServerApp := noErr { we found it! } ELSE FindASFileServerApp := fnfErr; { we didn't find it } END; {$S Initialize} FUNCTION GetServerType: Integer; VAR scPB: SCParamBlockRec; BEGIN scPB.versionPB.scExtNamePtr := NIL; scPB.versionPB.scCode := SCServerVersion; IF SyncServerDispatch(@scPB) = noErr THEN ;{ do nothing } GetServerType := scPB.versionPB.scServerType; END; {$S Initialize} PROCEDURE IncreaseStackSize (extraBytes: Size); BEGIN SetApplLimit(Ptr(ORD4(GetApplLimit) - extraBytes)); END; {$S Initialize} PROCEDURE InitializeApp; VAR vers: LongInt; err: OSErr; aLong: LongInt; ownerName: StringHandle; savedResFile: Integer; i: Integer; curVersion: VersRecHndl; BEGIN gQuit := FALSE; gSleep := kMaxSleep; {sleep until we have something to do} gPPCPortOpen := FALSE; gPPCPortRefNum := 0; gPPCSessRefNum := 0; gPPCDataRead := FALSE; gPPCWriteInProgress := FALSE; InitGraf(@qd.thePort); err := GetCurrentProcess(gOurPSN); { so completion routines can wake us up } IF (ORD4(GetApplLimit) - ORD4(GetZone^.bkLim)) < 2048 THEN BEGIN NotifyAndExit(kCantIncreaseStack); { ••• Bail out now ••• } Exit(InitializeApp); END; IncreaseStackSize(2048); { increase stack by 2K } MaxApplZone; IF NOT TrapAvailable(_Gestalt) THEN BEGIN { If Gestalt isn't available, then we can't even notify the user } { because we can't see if the Notification Manager is available } SysBeep(1); { so ring the bell } ExitToShell; { and exit } END; { see if Notification Manager is available to display error messages } gNotificationMgrPresent := Gestalt(gestaltNotificationMgrAttr, aLong) = noErr; FOR i := 1 TO kNumExitErrors DO GetIndString(gNMStrs[i], kExitErrorStrings, i); { Check system version } vers := 0; err := Gestalt(gestaltSystemVersion, vers); IF LoWord(vers) < $0700 THEN BEGIN NotifyAndExit(kExitNoSystem7); { ••• Bail out now ••• } Exit(InitializeApp); END; { Check this machine for AppleEvents. } { If they are not here, then we exit } IF (Gestalt(gestaltAppleEventsAttr, aLong) <> noErr) THEN BEGIN NotifyAndExit(kExitNoAppleEvts); { ••• Bail out now ••• } Exit(InitializeApp); END; IF NOT InitAEStuff THEN Exit(InitializeApp); { Make sure ServerDispatch trap is available } IF NOT TrapAvailable(ServerDispatch) THEN BEGIN NotifyAndExit(kExitNoServerDispatch); { ••• Bail out now ••• } Exit(InitializeApp); END; gServerType := GetServerType; IF gServerType = 1 THEN { we need to know where the AS File Server application is } { in case we need to launch it. } IF FindASFileServerApp(gASFileServerFSSpec) <> noErr THEN BEGIN NotifyAndExit(kExitCantFindASFSApp); { ••• Bail out now ••• } Exit(InitializeApp); END; { Get the Macintosh owner's name from the System file } { If we can't get it, then exit because we have to have it } { to ensure the owner is the remote user } { ••••• Although I get the name here, I should probably hook into the } { ••••• AppleTalk transition queue to catch any name changes. } savedResFile := CurResFile; UseResFile(0); ownerName := StringHandle(GetResource('STR ', -16096)); UseResFile(savedResFile); IF ownerName <> NIL THEN BEGIN { keep a global copy of the owner name } gOwnerName := ownerName^^; { and release the ownerName resource } ReleaseResource(Handle(ownerName)); END ELSE BEGIN NotifyAndExit(kExitNoOwnerName); { ••• Bail out now ••• } Exit(InitializeApp); END; IF NOT InitPPCStuff THEN Exit(InitializeApp); END; {PROCEDURE _DataInit;} {External;} { this is the MPW application initialization code } {$S Main} BEGIN {UnloadSeg(@_DataInit);} { throw out the setup code } InitializeApp; UnloadSeg(@InitializeApp); { get rid of my initialization code } DoEventLoop; PPCShutDown; END. { Main }